import geopandas as gpd
import pandas as pd
from shapely.geometry import Point, Polygon, LineString, MultiPoint, MultiPolygon, MultiLineString
from shapely.ops import unary_union, cascaded_union
import networkx as nx
import osmnx as ox
from typing import List, Dict, Tuple, Any, Optional
import numpy as np
import logging

logger = logging.getLogger(__name__)

class GeoProcessingTools:
    """地理处理工具类"""
    
    def __init__(self):
        pass
    
    def create_buffer(self, geometry_data: List[Dict], buffer_distance: float) -> gpd.GeoDataFrame:
        """
        创建缓冲区
        
        Args:
            geometry_data: 几何数据列表
            buffer_distance: 缓冲区距离（米）
            
        Returns:
            缓冲区几何数据
        """
        try:
            gdf = gpd.GeoDataFrame(geometry_data)
            gdf['geometry'] = gdf['geometry'].buffer(buffer_distance)
            logger.info(f"成功创建 {buffer_distance} 米缓冲区")
            return gdf
        except Exception as e:
            logger.error(f"创建缓冲区失败: {e}")
            raise
    
    def spatial_intersection(self, gdf1: gpd.GeoDataFrame, gdf2: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
        """
        空间相交分析
        
        Args:
            gdf1: 第一个地理数据框
            gdf2: 第二个地理数据框
            
        Returns:
            相交结果
        """
        try:
            result = gpd.overlay(gdf1, gdf2, how='intersection')
            logger.info("空间相交分析完成")
            return result
        except Exception as e:
            logger.error(f"空间相交分析失败: {e}")
            raise
    
    def spatial_union(self, gdf1: gpd.GeoDataFrame, gdf2: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
        """
        空间联合分析
        
        Args:
            gdf1: 第一个地理数据框
            gdf2: 第二个地理数据框
            
        Returns:
            联合结果
        """
        try:
            result = gpd.overlay(gdf1, gdf2, how='union')
            logger.info("空间联合分析完成")
            return result
        except Exception as e:
            logger.error(f"空间联合分析失败: {e}")
            raise
    
    def spatial_difference(self, gdf1: gpd.GeoDataFrame, gdf2: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
        """
        空间差异分析
        
        Args:
            gdf1: 第一个地理数据框
            gdf2: 第二个地理数据框
            
        Returns:
            差异结果
        """
        try:
            result = gpd.overlay(gdf1, gdf2, how='difference')
            logger.info("空间差异分析完成")
            return result
        except Exception as e:
            logger.error(f"空间差异分析失败: {e}")
            raise
    
    def dissolve_geometries(self, gdf: gpd.GeoDataFrame, by_column: str = None) -> gpd.GeoDataFrame:
        """
        几何体融合
        
        Args:
            gdf: 地理数据框
            by_column: 按某列分组融合
            
        Returns:
            融合后的几何数据
        """
        try:
            if by_column:
                result = gdf.dissolve(by=by_column)
            else:
                result = gdf.dissolve()
            logger.info("几何体融合完成")
            return result
        except Exception as e:
            logger.error(f"几何体融合失败: {e}")
            raise
    
    def clip_by_mask(self, gdf: gpd.GeoDataFrame, mask_gdf: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
        """
        按掩膜裁剪
        
        Args:
            gdf: 待裁剪的地理数据框
            mask_gdf: 掩膜数据框
            
        Returns:
            裁剪后的数据
        """
        try:
            result = gpd.clip(gdf, mask_gdf)
            logger.info("按掩膜裁剪完成")
            return result
        except Exception as e:
            logger.error(f"按掩膜裁剪失败: {e}")
            raise
    
    def calculate_area(self, gdf: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
        """
        计算面积
        
        Args:
            gdf: 地理数据框
            
        Returns:
            包含面积信息的数据框
        """
        try:
            gdf = gdf.copy()
            gdf['area'] = gdf.geometry.area
            logger.info("面积计算完成")
            return gdf
        except Exception as e:
            logger.error(f"面积计算失败: {e}")
            raise
    
    def calculate_length(self, gdf: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
        """
        计算长度
        
        Args:
            gdf: 地理数据框
            
        Returns:
            包含长度信息的数据框
        """
        try:
            gdf = gdf.copy()
            gdf['length'] = gdf.geometry.length
            logger.info("长度计算完成")
            return gdf
        except Exception as e:
            logger.error(f"长度计算失败: {e}")
            raise
    
    def calculate_distance(self, gdf1: gpd.GeoDataFrame, gdf2: gpd.GeoDataFrame) -> np.ndarray:
        """
        计算距离矩阵
        
        Args:
            gdf1: 第一组几何体
            gdf2: 第二组几何体
            
        Returns:
            距离矩阵
        """
        try:
            distances = gdf1.geometry.apply(lambda geom: gdf2.geometry.distance(geom))
            logger.info("距离计算完成")
            return distances.values
        except Exception as e:
            logger.error(f"距离计算失败: {e}")
            raise
    
    def create_voronoi_diagram(self, points: List[Tuple[float, float]], 
                             boundary: Polygon = None) -> gpd.GeoDataFrame:
        """
        创建泰森多边形
        
        Args:
            points: 点坐标列表
            boundary: 边界多边形
            
        Returns:
            泰森多边形数据
        """
        try:
            from scipy.spatial import Voronoi
            import matplotlib.pyplot as plt
            
            vor = Voronoi(points)
            polygons = []
            
            for i, region in enumerate(vor.regions):
                if not region or -1 in region:
                    continue
                polygon_points = [vor.vertices[j] for j in region]
                if len(polygon_points) >= 3:
                    poly = Polygon(polygon_points)
                    if boundary:
                        poly = poly.intersection(boundary)
                    polygons.append(poly)
            
            gdf = gpd.GeoDataFrame(geometry=polygons)
            logger.info("泰森多边形创建完成")
            return gdf
        except Exception as e:
            logger.error(f"泰森多边形创建失败: {e}")
            raise
    
    def create_convex_hull(self, gdf: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
        """
        创建凸包
        
        Args:
            gdf: 地理数据框
            
        Returns:
            凸包几何数据
        """
        try:
            all_geoms = gdf.geometry.tolist()
            union_geom = unary_union(all_geoms)
            convex_hull = union_geom.convex_hull
            result = gpd.GeoDataFrame(geometry=[convex_hull])
            logger.info("凸包创建完成")
            return result
        except Exception as e:
            logger.error(f"凸包创建失败: {e}")
            raise
    
    def simplify_geometry(self, gdf: gpd.GeoDataFrame, tolerance: float) -> gpd.GeoDataFrame:
        """
        简化几何图形
        
        Args:
            gdf: 地理数据框
            tolerance: 简化容差
            
        Returns:
            简化后的几何数据
        """
        try:
            gdf = gdf.copy()
            gdf['geometry'] = gdf.geometry.simplify(tolerance)
            logger.info(f"几何图形简化完成，容差: {tolerance}")
            return gdf
        except Exception as e:
            logger.error(f"几何图形简化失败: {e}")
            raise
    
    def create_grid(self, bounds: Tuple[float, float, float, float], 
                   cell_size: float) -> gpd.GeoDataFrame:
        """
        创建格网
        
        Args:
            bounds: 边界 (minx, miny, maxx, maxy)
            cell_size: 格网大小
            
        Returns:
            格网数据
        """
        try:
            minx, miny, maxx, maxy = bounds
            rows = int(np.ceil((maxy - miny) / cell_size))
            cols = int(np.ceil((maxx - minx) / cell_size))
            
            polygons = []
            for i in range(rows):
                for j in range(cols):
                    x_left = minx + j * cell_size
                    x_right = minx + (j + 1) * cell_size
                    y_bottom = miny + i * cell_size
                    y_top = miny + (i + 1) * cell_size
                    
                    polygon = Polygon([
                        (x_left, y_bottom),
                        (x_right, y_bottom),
                        (x_right, y_top),
                        (x_left, y_top)
                    ])
                    polygons.append(polygon)
            
            gdf = gpd.GeoDataFrame(geometry=polygons)
            logger.info(f"格网创建完成，{rows}x{cols} 个格子")
            return gdf
        except Exception as e:
            logger.error(f"格网创建失败: {e}")
            raise

# 全局地理处理工具实例
geo_processing_tools = GeoProcessingTools() 